To create output signals with the expected accuracy over the whole engine
speed range is not easily possible even with 32bit timers.
After investigating several approaches, I came up with the following
solution:
- One of the four timer channels is used to create an angular clock (e.g.
256 ticks per tooth) depending on engine speed
- the output of this angular clock is (physically) connected as input to
two other timer channels which are configured to counter mode with
external clock input
- On each of these timers, the first two match events are used to create
edges on two output channels each (2x2 = 4)
If a new engine speed N is set, the tooth period is calculated as follows:
... where ftimer is the timer frequency of 100MHz, N is engine speed (in rotations per minute) and m is the number of teeth per rotation (typically 60).
For m = 60, this is reduced
to
Now within this tooth period a given number X
(256) of ticks must be created
The problem is that rounding errors will occur which would lead to a wrong
tooth period.
E.g. with ftimer=
100MHz, N = 515, m
= 60 (rounded up):
If this is truncated to 758, the resulting tooth period after 256 ticks
would be
which is quite different from the correct value 194175.
To avoid this inaccuracy, the following approach is used:
First the remainder r of the
division (modulo,
the "%" operator in C ) is calculated
e.g.
Now instead of simply repeating the tick period X
(256) times, it is split as follows:
Or with the given values:
Where
These X (256) periods are created
with two DMA transfer descriptors which are linked (after transfer 1 is
finished, it's linked to transfer descriptor 2, if transfer 2 is finished,
it's linked to transfer descriptor 1).
So when a new engine speed is requested, the tick period is calculated and
then the two tick periods (ttick and ttick+1) and the
two repeat counters (error and X-error) have to be entered into the two
linked DMA transfer descriptors.
To avoid consistency issues during the update, a double buffering strategy
is used where the new transfer descriptors are prepared offline and then the
"next" entry of the currently active transfer descriptor is patched to point
to the offline descriptor.